package com.zs.letcode.hash_table;

import java.util.*;

/**
 * 寻找重复的子树
 * 给定一棵二叉树，返回所有重复的子树。对于同一类的重复子树，你只需要返回其中任意一棵的根结点即可。
 * <p>
 * 两棵树重复是指它们具有相同的结构以及相同的结点值。
 * <p>
 * 示例 1：
 * <p>
 * 1
 * / \
 * 2   3
 * /   / \
 * 4   2   4
 * /
 * 4
 * 下面是两个重复的子树：
 * <p>
 * 2
 * /
 * 4
 * 和
 * <p>
 * 4
 * 因此，你需要以列表的形式返回上述重复子树的根结点。
 * <p>
 * 相关标签
 * 树
 * 深度优先搜索
 * 广度优先搜索
 * 二叉树
 * <p>
 * 作者：力扣 (LeetCode)
 * 链接：https://leetcode-cn.com/leetbook/read/hash-table/xxm0i6/
 * 来源：力扣（LeetCode）
 * 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。
 *
 * @author madison
 * @description
 * @date 2021/7/3 09:22
 */
public class Chapter15 {
    public static void main(String[] args) {

    }

    /**
     * Definition for a binary tree node.
     */
    private class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;

        TreeNode() {
        }

        TreeNode(int val) {
            this.val = val;
        }

        TreeNode(int val, TreeNode left, TreeNode right) {
            this.val = val;
            this.left = left;
            this.right = right;
        }
    }

    private class Solution {
        /**
         * 方法一：深度优先搜索【通过】
         */
        Map<String, Integer> count;
        List<TreeNode> ans;

        public List<TreeNode> findDuplicateSubtrees(TreeNode root) {
            count = new HashMap<>();
            ans = new ArrayList<>();
            collect(root);
            return ans;
        }

        private String collect(TreeNode node) {
            if (Objects.isNull(node)) {
                return "#";
            }
            String serial = node.val + "," + collect(node.left) + "," + collect(node.right);
            count.put(serial, count.getOrDefault(serial, 0) + 1);
            if (count.get(serial) == 2) {
                ans.add(node);
            }
            return serial;
        }

        /**
         * 方法二：唯一标识符【通过】
         */
        int t;
        Map<String, Integer> trees;
        Map<Integer, Integer> counts;

        public List<TreeNode> findDuplicateSubtrees1(TreeNode root) {
            t = 1;
            trees = new HashMap<>();
            counts = new HashMap<>();
            ans = new ArrayList<>();
            lookup(root);
            return ans;
        }

        private int lookup(TreeNode node) {
            if (Objects.isNull(node)) {
                return 0;
            }
            String serial = node.val + "," + lookup(node.left) + "," + lookup(node.right);
            int uid = trees.computeIfAbsent(serial, x -> t++);
            counts.put(uid, counts.getOrDefault(uid, 0) + 1);
            if (counts.get(uid) == 2) {
                ans.add(node);
            }
            return uid;
        }
    }
}
